<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no, viewport-fit=cover"/>
<meta name="description" content="Interactive demonstration of hierarchical layout features by the LayeredDigraphLayout class."/> 
<link rel="stylesheet" href="../assets/css/style.css"/> 
<!-- Copyright 1998-2021 by Northwoods Software Corporation. -->
<title>Layered Digraph Layout</title>
</head>

<body>
  <!-- This top nav is not part of the sample code -->
  <nav id="navTop" class="w-full z-30 top-0 text-white bg-nwoods-primary">
    <div class="w-full container max-w-screen-lg mx-auto flex flex-wrap sm:flex-nowrap items-center justify-between mt-0 py-2">
      <div class="md:pl-4">
        <a class="text-white hover:text-white no-underline hover:no-underline
        font-bold text-2xl lg:text-4xl rounded-lg hover:bg-nwoods-secondary " href="../">
          <h1 class="mb-0 p-1 ">GoJS</h1>
        </a>
      </div>
      <button id="topnavButton" class="rounded-lg sm:hidden focus:outline-none focus:ring" aria-label="Navigation">
        <svg fill="currentColor" viewBox="0 0 20 20" class="w-6 h-6">
          <path id="topnavOpen" fill-rule="evenodd" d="M3 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 10a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM9 15a1 1 0 011-1h6a1 1 0 110 2h-6a1 1 0 01-1-1z" clip-rule="evenodd"></path>
          <path id="topnavClosed" class="hidden" fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"></path>
        </svg>
      </button>
      <div id="topnavList" class="hidden lg:text-base sm:block items-center w-auto mt-0 text-white p-0 z-20">
        <ul class="list-reset list-none font-semibold flex justify-end flex-wrap sm:flex-nowrap items-center px-0 pb-0">
          <li class="p-1 sm:p-0"><a class="topnav-link" href="../learn/">Learn</a></li>
          <li class="p-1 sm:p-0"><a class="topnav-link" href="../samples/">Samples</a></li>
          <li class="p-1 sm:p-0"><a class="topnav-link" href="../intro/">Intro</a></li>
          <li class="p-1 sm:p-0"><a class="topnav-link" href="../api/">API</a></li>
          <li class="p-1 sm:p-0"><a class="topnav-link" href="https://www.nwoods.com/products/register.html">Register</a></li>
          <li class="p-1 sm:p-0"><a class="topnav-link" href="../download.html">Download</a></li>
          <li class="p-1 sm:p-0"><a class="topnav-link" href="https://forum.nwoods.com/c/gojs/11">Forum</a></li>
          <li class="p-1 sm:p-0"><a class="topnav-link" href="https://www.nwoods.com/contact.html"
           target="_blank" rel="noopener" onclick="getOutboundLink('https://www.nwoods.com/contact.html', 'contact');">Contact</a></li>
          <li class="p-1 sm:p-0"><a class="topnav-link" href="https://www.nwoods.com/sales/index.html"
           target="_blank" rel="noopener" onclick="getOutboundLink('https://www.nwoods.com/sales/index.html', 'buy');">Buy</a></li>
        </ul>
      </div>
    </div>
    <hr class="border-b border-gray-600 opacity-50 my-0 py-0" />
  </nav>
  <div class="md:flex flex-col md:flex-row md:min-h-screen w-full max-w-screen-xl mx-auto">
    <div id="navSide" class="flex flex-col w-full md:w-48 text-gray-700 bg-white flex-shrink-0"></div>
    <!-- * * * * * * * * * * * * * -->
    <!-- Start of GoJS sample code -->
    
    <script src="../release/go.js"></script>
    <div class="p-4 w-full">
    <script id="code">
    function init() {
      var $ = go.GraphObject.make;  // for conciseness in defining templates

      myDiagram =
        $(go.Diagram, "myDiagramDiv",  // must be the ID or reference to div
          {
            initialAutoScale: go.Diagram.UniformToFill,
            layout: $(go.LayeredDigraphLayout)
            // other Layout properties are set by the layout function, defined below
          });

      // define the Node template
      myDiagram.nodeTemplate =
        $(go.Node, "Spot",
          { locationSpot: go.Spot.Center },
          $(go.Shape, "Rectangle",
            {
              fill: "lightgray",  // the initial value, but data binding may provide different value
              stroke: null,
              desiredSize: new go.Size(30, 30)
            },
            new go.Binding("fill", "fill")),
          $(go.TextBlock,
            new go.Binding("text", "text"))
        );

      // define the Link template to be minimal
      myDiagram.linkTemplate =
        $(go.Link,
          { selectable: false },
          $(go.Shape,
            { strokeWidth: 3, stroke: "#333" }));

      // generate a tree with the default values
      rebuildGraph();
    }

    function rebuildGraph() {
      var minNodes = document.getElementById("minNodes").value;
      minNodes = parseInt(minNodes, 10);

      var maxNodes = document.getElementById("maxNodes").value;
      maxNodes = parseInt(maxNodes, 10);

      generateDigraph(minNodes, maxNodes);
    }

    function generateDigraph(minNodes, maxNodes) {
      myDiagram.startTransaction("generateDigraph");
      // replace the diagram's model's nodeDataArray
      generateNodes(minNodes, maxNodes);
      // replace the diagram's model's linkDataArray
      generateLinks();
      // force a diagram layout
      layout();
      myDiagram.commitTransaction("generateDigraph");
    }

    // Creates a random number of randomly colored nodes.
    function generateNodes(minNodes, maxNodes) {
      var nodeArray = [];
      // get the values from the fields and create a random number of nodes within the range
      var min = parseInt(minNodes, 10);
      var max = parseInt(maxNodes, 10);
      if (isNaN(min)) min = 0;
      if (isNaN(max) || max < min) max = min;
      var numNodes = Math.floor(Math.random() * (max - min + 1)) + min;
      var i;
      for (i = 0; i < numNodes; i++) {
        nodeArray.push({
          key: i,
          text: i.toString(),
          fill: go.Brush.randomColor()
        });
      }

      // randomize the node data
      for (i = 0; i < nodeArray.length; i++) {
        var swap = Math.floor(Math.random() * nodeArray.length);
        var temp = nodeArray[swap];
        nodeArray[swap] = nodeArray[i];
        nodeArray[i] = temp;
      }

      // set the nodeDataArray to this array of objects
      myDiagram.model.nodeDataArray = nodeArray;
    }

    // Create some link data
    function generateLinks() {
      if (myDiagram.nodes.count < 2) return;
      var linkArray = [];
      var nit = myDiagram.nodes;
      var nodes = new go.List(/*go.Node*/);
      nodes.addAll(nit);
      for (var i = 0; i < nodes.count - 1; i++) {
        var from = nodes.get(i);
        var numto = Math.floor(1 + (Math.random() * 3) / 2);
        for (var j = 0; j < numto; j++) {
          var idx = Math.floor(i + 5 + Math.random() * 10);
          if (idx >= nodes.count) idx = i + (Math.random() * (nodes.count - i)) | 0;
          var to = nodes.get(idx);
          linkArray.push({ from: from.data.key, to: to.data.key });
        }
      }
      myDiagram.model.linkDataArray = linkArray;
    }

    function layout() {
      myDiagram.startTransaction("change Layout");
      var lay = myDiagram.layout;

      var direction = getRadioValue("direction");
      direction = parseFloat(direction, 10);
      lay.direction = direction;

      var layerSpacing = document.getElementById("layerSpacing").value;
      layerSpacing = parseFloat(layerSpacing, 10);
      lay.layerSpacing = layerSpacing;

      var columnSpacing = document.getElementById("columnSpacing").value;
      columnSpacing = parseFloat(columnSpacing, 10);
      lay.columnSpacing = columnSpacing;

      var cycleRemove = getRadioValue("cycleRemove");
      if (cycleRemove === "CycleDepthFirst") lay.cycleRemoveOption = go.LayeredDigraphLayout.CycleDepthFirst;
      else if (cycleRemove === "CycleGreedy") lay.cycleRemoveOption = go.LayeredDigraphLayout.CycleGreedy;

      var layering = getRadioValue("layering");
      if (layering === "LayerOptimalLinkLength") lay.layeringOption = go.LayeredDigraphLayout.LayerOptimalLinkLength;
      else if (layering === "LayerLongestPathSource") lay.layeringOption = go.LayeredDigraphLayout.LayerLongestPathSource;
      else if (layering === "LayerLongestPathSink") lay.layeringOption = go.LayeredDigraphLayout.LayerLongestPathSink;

      var initialize = getRadioValue("initialize");
      if (initialize === "InitDepthFirstOut") lay.initializeOption = go.LayeredDigraphLayout.InitDepthFirstOut;
      else if (initialize === "InitDepthFirstIn") lay.initializeOption = go.LayeredDigraphLayout.InitDepthFirstIn;
      else if (initialize === "InitNaive") lay.initializeOption = go.LayeredDigraphLayout.InitNaive;

      var aggressive = getRadioValue("aggressive");
      if (aggressive === "AggressiveLess") lay.aggressiveOption = go.LayeredDigraphLayout.AggressiveLess;
      else if (aggressive === "AggressiveNone") lay.aggressiveOption = go.LayeredDigraphLayout.AggressiveNone;
      else if (aggressive === "AggressiveMore") lay.aggressiveOption = go.LayeredDigraphLayout.AggressiveMore;

      //TODO implement pack option
      var pack = document.getElementsByName("pack");
      var packing = 0;
      for (var i = 0; i < pack.length; i++) {
        if (pack[i].checked) packing = packing | parseInt(pack[i].value, 10);
      }
      lay.packOption = packing;

      var setsPortSpots = document.getElementById("setsPortSpots");
      lay.setsPortSpots = setsPortSpots.checked;

      myDiagram.commitTransaction("change Layout");
    }

    function getRadioValue(name) {
      var radio = document.getElementsByName(name);
      for (var i = 0; i < radio.length; i++)
        if (radio[i].checked) return radio[i].value;
    }
    window.addEventListener('DOMContentLoaded', init);
  </script>

<div id="sample">
  <div style="margin-bottom: 5px; padding: 5px; background-color: aliceblue">
    <span style="display: inline-block; vertical-align: top; padding: 5px">
      <b>New Graph</b><br />
      MinNodes: <input type="text" size="3" id="minNodes" value="20" /><br />
      MaxNodes: <input type="text" size="3" id="maxNodes" value="100" /><br />
      <button type="button" onclick="rebuildGraph()">Generate Digraph</button>
    </span>
    <span style="display: inline-block; vertical-align: top; padding: 5px">
      <b>LayeredDigraphLayout Properties</b><br />
      Direction:
      <input type="radio" name="direction" onclick="layout()" value="0" checked="checked" />Right (0)
      <input type="radio" name="direction" onclick="layout()" value="90" />Down (90)
      <input type="radio" name="direction" onclick="layout()" value="180" />Left (180)
      <input type="radio" name="direction" onclick="layout()" value="270" />Up (270)<br />
      LayerSpacing:
      <input type="text" size="3" id="layerSpacing" value="25" onchange="layout()" style="clear: left;" /><br />
      ColumnSpacing:
      <input type="text" size="3" id="columnSpacing" value="25" onchange="layout()" /><br />
      CycleRemove:
      <input type="radio" name="cycleRemove" onclick="layout()" value="CycleDepthFirst" checked="checked" /> CycleDepthFirst
      <input type="radio" name="cycleRemove" onclick="layout()" value="CycleGreedy" /> CycleGreedy<br />
      Layering:
      <input type="radio" name="layering" onclick="layout()" value="LayerOptimalLinkLength" checked="checked" /> LayerOptimalLinkLength
      <input type="radio" name="layering" onclick="layout()" value="LayerLongestPathSource" /> LayerLongestPathSource
      <input type="radio" name="layering" onclick="layout()" value="LayerLongestPathSink" /> LayerLongestPathSink<br />
      Initialize:
      <input type="radio" name="initialize" onclick="layout()" value="InitDepthFirstOut" checked="checked" /> InitDepthFirstOut
      <input type="radio" name="initialize" onclick="layout()" value="InitDepthFirstIn" /> InitDepthFirstIn
      <input type="radio" name="initialize" onclick="layout()" value="InitNaive" /> InitNaive<br />
      Aggressive:
      <input type="radio" name="aggressive" onclick="layout()" value="AggressiveNone" /> AggressiveNone
      <input type="radio" name="aggressive" onclick="layout()" value="AggressiveLess" checked="checked" /> AggressiveLess
      <input type="radio" name="aggressive" onclick="layout()" value="AggressiveMore" /> AggressiveMore<br />
      Pack:
      <input type="checkbox" name="pack" onclick="layout()" value="4" checked="checked" /> PackMedian
      <input type="checkbox" name="pack" onclick="layout()" value="2" checked="checked" /> PackStraighten
      <input type="checkbox" name="pack" onclick="layout()" value="1" checked="checked" /> PackExpand<br />
      SetsPortSpots: <input type="checkbox" id="setsPortSpots" onclick="layout()" checked="checked" />
    </span>
  </div>
  <div id="myDiagramDiv" style="border: solid 1px black; background: white; width: 100%; height: 500px"></div>
  <p>
    For information on <b>LayeredDigraphLayout</b> and its properties, see the <a>LayeredDigraphLayout</a> documentation page.
  </p>
</div>
    </div>
    <!-- * * * * * * * * * * * * * -->
    <!--  End of GoJS sample code  -->
  </div>
</body>
<!--  This script is part of the gojs.net website, and is not needed to run the sample -->
<script src="../assets/js/goSamples.js"></script>
</html>
